﻿using Avalon.Core.Data;
using Avalon.Core.MVC.Attributes;
using Avalon.Core.Providers;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web.Mvc;

namespace Avalon.Core.MVC.Helpers {
    public static class Extensions {
        public static ErrorMessage GetErrorMessage(this ModelState modelState) {
            ErrorMessage result = new ErrorMessage();
            if (modelState != null && modelState.Errors.Count > 0) {
                result.ErrorClass = "control-group error";
                foreach (var message in modelState.Errors) {
                    result.Message += message.ErrorMessage;
                }
            }
            return result;
        }

        public static string GetDateTimeValue(this ModelMetadata propertyMetadata) {
            if (propertyMetadata.Model is DateTime) {
                var displayvalue =
                    propertyMetadata.Model != null && (DateTime)propertyMetadata.Model != DateTime.MinValue ?
                    ((DateTime)propertyMetadata.Model).ToString("dddd dd MMMM yyyy") :
                    DateTime.Now.ToString("dddd dd MMMM yyyy");
                return displayvalue;
            }
            else {
                return "n.a.";
            }
        }

        public static string GetTitle(this ModelMetadata propertyMetadata) {
            if (propertyMetadata.IsEntityProperty()) {
                var modelName = System.Data.Objects.ObjectContext.GetObjectType(propertyMetadata.ModelType).Name;
                return propertyMetadata.DisplayName == null ? modelName : propertyMetadata.DisplayName;
            }
            else {
                return propertyMetadata.DisplayName == null ? propertyMetadata.PropertyName : propertyMetadata.DisplayName;
            }
        }

        public static string NavigationKey(this ModelMetadata propertyMetadata) {
            string navigationPropertyName = "Id";
            if (propertyMetadata.AdditionalValues.ContainsKey(DetailNavigationAttribute.Key)) {
                navigationPropertyName = (String)propertyMetadata.AdditionalValues[DetailNavigationAttribute.Key];
            }
            return navigationPropertyName;
        }

        public static bool IsNavigationProperty(this ModelMetadata propertyMetadata) {
            bool parentModelContainsNavigationProperty = propertyMetadata.AdditionalValues.ContainsKey(DetailNavigationAttribute.Key);

            if (parentModelContainsNavigationProperty) {
                return true;
            }
            else {
                return propertyMetadata.PropertyName == "Name";
            }
        }

        public static bool IsParentProperty(this ModelMetadata propertyMetadata) {
            if (propertyMetadata.AdditionalValues.ContainsKey(IgnoreAttribute.Key)) return false;
            return propertyMetadata.AdditionalValues.ContainsKey(ParentAttribute.Key);
        }

        public static ModelMetadata GetParentProperty(this ModelMetadata modelMetadata) {
            return modelMetadata.Properties.FirstOrDefault(p => p.IsParentProperty());
        }

        public static string GetParentId(this ModelMetadata parentMetadata) {
            string parentId;
            if (parentMetadata.Model != null && parentMetadata.Model is IEntity) {
                parentId = ((IEntity)parentMetadata.Model).Id.ToString();
            }
            else {
                parentId = System.Web.HttpContext.Current.Request.QueryString["parentId"];
                if (string.IsNullOrWhiteSpace(parentId) && System.Web.HttpContext.Current.Request.Form != null) {
                    parentId = System.Web.HttpContext.Current.Request.Form["parentId"];
                }
            }
            return parentId;
        }

        public static bool IsCollectionProperty(this ModelMetadata propertyMetadata) {
            if (propertyMetadata.AdditionalValues.ContainsKey(IgnoreAttribute.Key)) return false;

            if (propertyMetadata.ModelType.IsGenericType) {
                return typeof(ICollection<>).IsAssignableFrom(propertyMetadata.ModelType.GetGenericTypeDefinition()) ||
                    typeof(List<>).IsAssignableFrom(propertyMetadata.ModelType.GetGenericTypeDefinition()) ||
                    typeof(HashSet<>).IsAssignableFrom(propertyMetadata.ModelType.GetGenericTypeDefinition()) ||
                    typeof(IQueryable<>).IsAssignableFrom(propertyMetadata.ModelType.GetGenericTypeDefinition()) ||
                    typeof(DbSet<>).IsAssignableFrom(propertyMetadata.ModelType.GetGenericTypeDefinition());
            }

            return false;
        }

        public static bool IsEntityProperty(this ModelMetadata propertyMetadata) {
            if (propertyMetadata.AdditionalValues.ContainsKey(IgnoreAttribute.Key)) return false;
            return typeof(IEntity).IsAssignableFrom(propertyMetadata.ModelType);
        }

        public static bool IsViewModelProperty(this ModelMetadata propertyMetadata) {
            return typeof(IViewModel).IsAssignableFrom(propertyMetadata.ModelType);
        }

        public static bool IsSimpleProperty(this ModelMetadata propertyMetadata) {
            return !propertyMetadata.IsCollectionProperty() &&
                !propertyMetadata.IsEntityProperty() &&
                !propertyMetadata.AdditionalValues.ContainsKey(IgnoreAttribute.Key);
        }

        public static ModelMetadata GetModelMetadata(this HtmlHelper htmlHelper) {
            return ModelMetadataProviders.Current.GetMetadataForType(() => htmlHelper.ViewData.Model, htmlHelper.ViewData.Model.GetType());
        }

        public static Uri GetEntityListUrl(this ModelMetadata metadata) {
            if (metadata.Model == null) return new Uri(string.Format("http://{0}", System.Web.HttpContext.Current.Request.Url.Authority));
            var link = string.Format("http://{0}/{1}",
                System.Web.HttpContext.Current.Request.Url.Authority,
                ControllerProvider.GetControllerName(metadata.ModelType));
            return new Uri(link);
        }

        public static Uri GetEntityDetailUrl(this ModelMetadata metadata) {
            if (metadata.Model == null) return new Uri(string.Format("http://{0}", System.Web.HttpContext.Current.Request.Url.Authority));
            else if (metadata.IsEntityProperty()) {
                var id = metadata.Properties.FirstOrDefault(p => p.PropertyName == "Id").Model;
                var link = string.Format("http://{0}/{1}/Detail/{2}",
                    System.Web.HttpContext.Current.Request.Url.Authority,
                    ControllerProvider.GetControllerName(metadata.ModelType), id);
                return new Uri(link);
            }
            else if (typeof(Guid).IsAssignableFrom(metadata.ModelType)) {
                var controllerName = metadata.AdditionalValues[ParentAttribute.Key].ToString();
                var id = metadata.Model;
                var link = string.Format("http://{0}/{1}/Detail/{2}", System.Web.HttpContext.Current.Request.Url.Authority, controllerName, id);
                return new Uri(link);
            }
            else {
                return null;
            }
        }

        public static Uri GetEntityCreateUrl(this ModelMetadata metadata, IEntity parent) {
            string link = "";
            if (parent != null) {
                link = string.Format("http://{0}/{1}/CreateReferencedEntity?parentId={2}&parentName={3}",
                    System.Web.HttpContext.Current.Request.Url.Authority,
                    ControllerProvider.GetControllerName(metadata.ModelType),
                    parent.Id,
                    System.Data.Objects.ObjectContext.GetObjectType(parent.GetType()).Name);
            }
            else {
                link = string.Format("http://{0}/{1}/Create",
                    System.Web.HttpContext.Current.Request.Url.Authority,
                    ControllerProvider.GetControllerName(metadata.ModelType));

            }
            return new Uri(link);
        }

        public static Uri GetEntityEditUrl(this ModelMetadata metadata) {
            string link = string.Format("http://{0}/{1}/Edit/{2}",
                System.Web.HttpContext.Current.Request.Url.Authority,
                ControllerProvider.GetControllerName(metadata.ModelType),
                ((IEntity)metadata.Model).Id);

            return new Uri(link);
        }



        public static ApplicationMetadataType GetMetadataType(this ModelMetadata propertyMetadata) {
            if (propertyMetadata.IsParentProperty()) return ApplicationMetadataType.Parent;
            if (propertyMetadata.IsEntityProperty()) return ApplicationMetadataType.Reference;
            if (propertyMetadata.IsViewModelProperty()) return ApplicationMetadataType.Reference;
            if (propertyMetadata.IsCollectionProperty()) return ApplicationMetadataType.Grid;
            return ApplicationMetadataType.SimpleValue;
        }
    }

    public enum ApplicationMetadataType {
        Parent,
        SimpleValue,
        ComplexValue,
        Reference,
        Grid,
        Table
    }
}
